home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
JCSM Shareware Collection 1993 November
/
JCSM Shareware Collection - 1993-11.iso
/
cl720
/
qbnws14j.lzh
/
QBNWS104.NWS
< prev
next >
Wrap
Text File
|
1990-09-03
|
83KB
|
1,808 lines
Volume 1, Number 4 September 3, 1990
**************************************************
* *
* QBNews *
* *
* International QuickBASIC Electronic *
* Newsleter *
* *
* Dedicated to promoting QuickBASIC around *
* the world *
* *
**************************************************
The QBNews is an electronic newsletter published by Clearware
Computing. It can be freely distributed providing NO CHARGE is charged
for distribution. The QBNews is copyrighted in full by Clearware
Computing. The authors hold the copyright to their individual
articles. All program code appearing in QBNews is released into the
public domain. You may do what you wish with the code except
copyright it. QBNews must be distributed whole and unmodified.
You can write The QBNews at:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
Copyright (c) 1990 by Clearware Computing.
The QBNews Page i
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
T A B L E O F C O N T E N T S
1. From the Editors Desk
CONTEST !!!! ................................................. 1
2. Beginners Corner
The BASICS of QB'S Serial Communications by Ranjit Aiyagari .. 2
3. Who ya gonna call? CALL INTERRUPT
Using a FOSSIL - Take 1 by Hector Plasmic .................... 6
Using a FOSSIL - Take 2 by Chris Wagner ..................... 7
4. Product Announcements
Index Manager for PDS 7 and Networks ......................... 10
Don Malin's Cross Reference Program .......................... 11
5. Power Programming
Peeking at DOS with P.D.Q. by Ethan Winer .................... 13
A Pop-Up Communications Program with PDQ by Dave Cleary ...... 18
A Pop-Up Calculator with Stay-Res by Larry Stone ............. 20
6. Algorithms
Improving your IQUEUE by Jim Mack ............................ 23
7. Input Past End
Get the QBNews on Disk ...................................... 29
Contacting the QBNews ....................................... 30
The QBNews Page ii
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
F r o m t h e E d i t o r s D e s k
----------------------------------------------------------------------
Free software to a lucky QBNews reader!!!
In order to gauge readership anmd distribution of this
newsletter, I am holding a contest. One lucky reader will get his
choice of either Crescent Softwares PDQComm or Don Malin's XREF. Both
are fine packages. A description of XREF is included in this issue's
Product announcement section. PDQComm is a replacement library for
QuickBASIC's internal Comm routines. It was also written by yours
truely. Originally, it was meant to add communications support to PDQ
but it also works fine with QuickBASIC and even has a far string
version for BC7. Contact Crescent Software at 203-438-5300 or the
Crescent Software Support BBS at 203-426-5958 for more info on these
or other Crescent products.
Now here are the rules. Send a postcard with your Name, Address,
and how you received your copy of the QBNews. I don't care if it is "I
downloaded it from XYZ bbs" or "I got it from my friend Bill", I would
just like to know. All entries must be postmarked by November 15,1990
and only 1 entry per address will be allowed. This contest is void
where prohibited by law. Winner will be notified and announced in the
November issue of the QBNews. Even if you aren't interrested in any of
the prizes, drop me a postcard anyways. I'd like to know if this
newsletter is reaching everybody who would want it.
That brings me to another topic regarding mail. It seems that all
the letters I get are from beginning to intermiediate programmers. The
QBNews tries to please everybody from beginner to "Power Programmer",
but I don't receive mail from these advanced users. I would like to
hear from you so you can let me know what you think about some of our
"advanced" articles. I know there are "Professional" QB programmers
out there and I would like to hear your views on the subject matter in
the QBNews also.
This issue is kind of on the large size due to the special TSR
programs you receive with it. I had to leave some sections out to keep
the size resonable. If you find that you missed a section not included
in this issue, let me know. I really would like to know what my
readers find interresting and what they don't.
Well, so long for another 3 months. Enjoy.
Dave Cleary
Send your postcards to:
The QBNews Reader Contest
P.O. Box 507
Sandy Hook, CT 06482
Remember to say how you received your copy.
The QBNews Page 1
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
B e g i n n e r s C o r n e r
----------------------------------------------------------------------
The BASICS of QB'S Serial Communications by Ranjit Aiyagari
QuickBASIC has always been recognized for having a unique
feature which most other languages (i.e. Pascal, C, Fortran) lacked:
full serial communications support built in to the language. In this
article, I'll explain BASIC's communications functions and, more
importantly, their uses. If you always wanted to write a BBS or a
terminal program in QuickBASIC but never had the faintest idea how,
keep reading.
In order to use the modem (or any other serial device) from
BASIC, you must first OPEN it, treating it as you would treat a file.
The flexible syntax of the OPEN COM statement is explained well in
Microsoft's manuals, but I'll give a most common syntax of it:
OPEN "COM1:2400,N,8,1,OP0,DS0" FOR RANDOM AS #FileNumber% LEN=1024
The above statement, as you can probably gather from its simple
syntax, will open communications port number 1 at a most common
setting of 2400 bps, 8 data bits, 1 stop bit, and no parity. The OP0
and DS0 parameters signify, respectively, for BASIC not to wait at
all before communications lines become active, and for BASIC to
ignore (for simplicity's sake) the DSR (Data Set Ready) line.
Lastly, the LEN parameter specifies the length of the input buffer.
Now that you've opened it, output to the modem is easy. To send
an "AT".. command, just do a PRINT #1, ModemCommand$. Getting input
from the modem, however, can be done in multiple ways:
Character by character: This is a method I prefer, getting
characters one at a time and dealing with them. To get one character
from the modem, use the INPUT$ function in the following manner:
Char$ = INPUT$(1, #FileNumber%) [where FileNumber% is of course the
file number under which you opened the communications port.
Everything waiting in the modem buffer: With this method, you
can store whatever is waiting in the input buffer in a string, and
then deal with the string as you wish using BASIC's string
manipulation functions. This is done by using the LOC function.
Normally returning the position of the last byte read for files, LOC
changes meaning when used with communications devices. Instead, it
returns the number of characters waiting in the input buffer. To
input the modem input buffer to a string, do the following:
ModemInput$ = INPUT$(LOC(FileNumber%), #FileNumber%). Now you can
use INSTR to figure out whether certain characters were returned, or
MID$ to extract part of the input.
A commonly asked question is "How do I recover from a carrier
drop?" Since the CD (carrier detect) option in the OPEN statement
The QBNews Page 2
Volume 1, Number 4 September 3, 1990
defaults to zero, your program seems to have no way of knowing
whether the other side is connected or not. The method I recommend
for this is using an ON TIMER routine, such as this:
ON TIMER(15) GOSUB CheckCarrier
' Remember to compile with /v when using ON TIMER
Now, we use a low-level method of checking whether the DCD
(data carrier detect) line is up or down. You must know the
communications port base address first, so to figure it out, do the
following (assuming the communications port is in a variable
ComPort%):
DEF SEG = &H40
BaseAddress% = PEEK((ComPort-1)*2) + PEEK((ComPort-1)*2+1)*256
' The base address is usually &H3F8 for COM1 and &H2F8 for COM2
Use the INP function:
CheckCarrier:
IF (INP(BaseAddress+6) AND 128) = 0 Then
Print "Carrier lost"
Connected% = 0
ELSE
Connected% = -1
END IF
RETURN
Another common question: "How do I wait for a ring and then
answer?" This is far simpler, and uses the EOF function, which
returns false if characters are waiting in the input buffer (You
should make sure to have the statement CONST False = 0, True = NOT
False at the top of your program). To wait for the ring:
PRINT #FileNumber%, "ATE0"
SLEEP 2 ' Wait for the modem to respond
Dummy$ = INPUT$(LOC(FileNumber%), #FileNumber%) ' Discard "OK"
ModemInput$ = ""
DO
IF NOT EOF(FileNumber%) THEN ' Characters are waiting, so get them
ModemInput$ = ModemInput$ + Input$(1, #FileNumber%)
END IF
LOOP UNTIL INKEY$ <> "" OR INSTR(ModemInput$, "RING")
Dummy$ = INPUT$(LOC(FileNumber%), #FileNumber%) ' Clear the buffer
' Now, to answer:
PRINT #FileNumber%, "ATA"
Lastly, probably the most frequently asked question about QB
communications: "How do I change the BPS rate while connected?" This
requires some low-level UART (Universal Asynchronous Receiver-
Transmitter) accessing [the UART is the chip in control of the serial
port]. The best use of this is to read the modem's "CONNECT..."
message and change the BPS rate accordingly. It requires a series of
The QBNews Page 3
Volume 1, Number 4 September 3, 1990
four OUT statements:
FUNCTION JustNumbers%(X$) STATIC
' Takes a string and returns the numeric value of the numeric
' characters inside it.
Temp$ = ""
FOR CheckString% = 1 TO LEN(X$)
Char$ = MID$(X$, CheckString%, 1)
IF INSTR("1234567890", Char$) THEN Temp$ = Temp$ + Char$
NEXT CheckString%
IF Temp$ = "" THEN JustNumbers% = -1 ELSE JustNumbers% = VAL(Temp$)
Temp$ = "": X$ = "" ' Empty the strings
END FUNCTION
' Assume we've just sent the "ATA"
ModemInput$ = ""
DO
IF NOT EOF(FileNumber%) THEN
ModemInput$ = ModemInput$ + INPUT$(1, #FileNumber%)
END IF
Connect% = INSTR(ModemInput$, "CONNECT")
LOOP UNTIL Connect% AND_
INSTR(Connect%, ModemInput$, CHR$(13)+CHR$(10))
NewBpsRate% = JustNumbers%(ModemInput$)
IF NewBpsRate% = -1 THEN NewBpsRate% = 300 ' Just a "CONNECT"
' Now, change the BPS rate. Since we already initialized at 2400
' don't bother changing it if the other side is also at 2400.
' Also, assume the base address is stored in BaseAddress%
' (discussed above)
IF NewBpsRate% <> 2400 THEN
OUT BaseAddress% + 3, (INP(BaseAddress% + 3) OR &H80)' Enable DLAB
OUT BaseAddress%, (115200 \ NewBpsRate%) MOD &H100 ' Send LSB
OUT BaseAddress% + 1, (115200 \ NewBpsRate%) \ &H100 ' Send MSB
OUT BaseAddress% + 3, (INP(BaseAddress% + 3) AND &H7F)'Disable DLAB
END IF
Now, for an explanation. The first and the fourth OUT
statements, as you can see, modify the DLAB, the divisor latch access
bit. The reason for this is that when the DLAB is enabled, the first
two registers (BaseAddress% and BaseAddress+1), instead of being the
receive buffer/transmit holding and interrupt enable registers,
change their meaning to the low and high byte of the BPS divisor
(also called the LSB and MSB). The BPS divisor is calculated by
dividing 115200, the maximum serial port speed, by the BPS rate
desired. Thus, these four statements will modify the BPS rate to any
value between 1 and 115200. This can be useful when writing both
terminal programs and BBS's, when you want your software to "fall
The QBNews Page 4
Volume 1, Number 4 September 3, 1990
back", that is, be able to transmit at any speed lower than the
highest.
If you need help on any topic dealing with serial communications
in QuickBASIC/BASIC PDS, please feel free to send me a message on the
echo. I've written BBS's and terminal programs in QuickBASIC, along
with small communications functions in Assembler, and probably would
be able to help you.
The QBNews Page 5
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
W h o y a g o n n a c a l l ? C A L L I N T E R R U P T
----------------------------------------------------------------------
Using a FOSSIL - Take 1 by Hector Plasmic
From FOSSIL.DOC (for version 5 FOSSILs):
"FOSSIL is an acronym for "Fido/Opus/SEAdog Standard Interface
Layer". To say that the concept has gained wide acceptance in the
FidoNet community would be an understatement... There are already
FOSSIL implementations for the Tandy 2000, Heath/Zenith 100, Sanyo 555
and other "non-IBM" architectures. With each new 'port' of the spec,
the potential of a properly coded FOSSIL application grows!"
QuickBASIC has some problems with modem communications. There
are ways around most of the problems; patch this, avoid that, use OUT,
use a third-party library, etc. But it can be a pain in the neck to
worry about ways around all the flaws in OPEN COM (and, in the case of
third-party libraries, expensive). FOSSIL will not only handle your
communications routines with Interrupt-driven buffered modem i/o, but
makes your programs a little more "generic" than they would be
otherwise.
FOSSIL drivers "steal" interrupt 14h (usually the BIOS's serial
communications interrupt) and install themselves instead. From that
point onward, calls to interrupt 14h go through the FOSSIL instead of
to the BIOS directly. The FOSSIL is basically a "BIOS extender" which
provides many more functions than the usual three or so provided.
While I won't attempt to cover every aspect of FOSSIL usage, I
thought I'd present a "FOSSIL package" for QB 4.x that you can use to
build your own programs that use the FOSSIL for communications. The
subroutines in the file FOSSIL1.BAS assume that you know the baud rate
and commport number you're dealing with, although the FOSSIL will use
the commport at whatever baud it's already set at...all you really
need to know is if you're operating local-only or online.
For more information on the FOSSIL, download one and look for the
enclosed FOSSILV5.ARC inside the archive. It will contain FOSSIL.DOC
and FOSSIL.CHT. These two documents should contain all the
information you need to utilize every aspect of the FOSSIL. A few of
the FOSSIL drivers available are:
X00.SYS (Ray Gwinn)
OPUS!COMM (Bob Hartman)
BNU (David Nugent)
The QBNews Page 6
Volume 1, Number 4 September 3, 1990
Using a FOSSIL - Take 2 by Chris Wagner
So what is this FOSSIL thing? Well, FOSSIL is an acronym for
"Fido/Opus/SEAdog Standard Interface Layer". What the FOSSIL does is
bypass the BIOS communications functions and replaces them with it's
own. By doing this, the programmer can concentrate on the software
without worrying about the hardware as much.
The FOSSIL itself is installed by adding a line in your
config.sys file that includes the FOSSIL device driver as well as a
few parameters for the FOSSIL such as transmit buffer size or locked
baud rate. For more information about the FOSSIL itself and
installation, read FOSSIL.DOC and FOSSIL.CHT. These files come with
the FOSSIL device driver which can be found on many public BBS's. To
obtain a complete fossil device driver package, look for a file called
X00____.___. Underscores are for version and archive type information
which will vary.
Once the FOSSIL is installed, you need to access it somehow.
That's where I come in. The program that I have included in
FOSSIL2.BAS is a terminal program that does all it's serial
communications via the fossil device driver. To use this program, you
must have INTERRUPT in your library, or load QB.LIB / QB.QLB for
proper operation.
What advantages does a FOSSIL give me? Well, there are many.
You have the ability to change baud rate WITHOUT closing the serial
channel, Baud rates up to 38400 directly supported, Status functions
like character waiting and carrier detect available, and the ability
to terminate a program WITHOUT dropping DTR. All of this can be done
using REGULAR QuickBASIC programming and no additional libraries.
In the included MYFOS.BAS program, I have coded the program so
that all of the SUB programs can be put directly into a library if
desired. This gives the ability to call FOSSIL functions from any of
your programs once they are in your library.
You will notice that many of the SUB programs have many
parameters that must be given. I did this so that they can be used in
any program from a library. The DataType Reg is used extensively in
the program, and is also passed to all of the SUB programs. This
allows for the INTERRUPT calls that access the FOSSIL.
In the following paragraphs, I will describe the SUB programs and
their parameters. Many of these SUB programs are very small, but have
many parameters that are of great importance.
Parser: This SUB is a command line parser (divider) that returns an
array of parameters that where given on the command line when
the program was run. Using parameters, you can start the
program with special options other than defaults.
AnsiPrint: All that this sub is for is to print data to the screen via
The QBNews Page 7
Volume 1, Number 4 September 3, 1990
the CONS: device allowing for ANSI graphics.
FossilInit: This SUB activates the fossil. It must be called before
ANY other fossil functions. This SUB will also automatically
raise DTR.
FossilDeInit: This SUB de-activates the FOSSIL. It should be called
when your program is finished using the fossil and before your
program terminates. The state of DTR is not affected by this
SUB.
SetHandShake: This SUB sets up the handshaking between Computer,
FOSSIL, and remote computer. There is many ways to use this,
but use wisely. I find that FOSSIL RTS/CTS works best for my
applications. Remember, in binary file transfers that the
characters that represent XON/XOFF will be embedded in data
and will cause problems. Therefore, XON/XOFF should be avoided
sometimes.
SetPortParams. This SUB has many parameters in order to make it
generic. It allows you to set the serial port Baud, Bits per
word, stop bits, and parity.
SetDTR: This sub allows you to raise or lower DTR. This is useful
to hang up a modem to terminate communications.
Transmit: This SUB is used to send transmit data to the serial port
one character at a time. It will return -2 (and clear the
output buffer) if the output buffer is filled. If this is
undesired (automatic clearing of the buffer) it can easily be
removed from the SUB.
Receive: This sub is used to get data from the serial port one
character at a time. It will return -1 if there is no data
waiting in the buffer. If the receive buffer is over-run, it
will be automatically cleared and a -2 will be returned.
Status: This SUB returns the status of the FOSSIL buffers and Carrier
Detect signal. It returns bitmapped data and is documented
in the SUB program.
BaudSelect: This sub is used to change BAUD rate only, but requires
all the parameters such as bits per word, parity, and stop
bits.
To demonstrate all of these SUB programs, I made yet another SUB
program called TERMINAL that you can also put in a library. The
terminal can be used to call BBS's or whatever as well as upload and
download files as long as you have the Zmodem protocol program DSZ in
your path. The commands that the terminal accepts are documented by
remarks in the source code.
[Editor's Note]
The QBNews Page 8
Volume 1, Number 4 September 3, 1990
Fossil1.bas and Fossil2.bas are included in the file Fossil.zip.
**********************************************************************
Chris Wagner is system operator of DREAMLINE, Conshococken PA (FidoNet
Address 1:273/708) (215) 825-8996. He can be reached there or in care
of this newsletter.
**********************************************************************
The QBNews Page 9
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
P r o d u c t A n n o u n c e m e n t s
----------------------------------------------------------------------
Index Manager for PDS 7 and Networks
INDEX MANAGER ALLOWS QUICKBASIC/PDS 7 NETWORK OPERATION
For years there has been a critical need for a simple, reasonably
priced way to run QuickBASIC on a network. This problem has now been
solved. Index Manager(tm), the popular $59 QuickBASIC B+ Tree file
indexing package, has been enhanced to support Microsoft BASIC
Professional Development System 7.0 and network use.
The PDS 7.0 version provides support for Microsoft's new "far
string" environment which eliminates BASIC's 64K data area limitation.
The Index Manager PDS 7.0 version sells for $99 and can be used with
either QuickBASIC or PDS 7.0 with no coding changes.
The Index Manager network version sells for $250 and allows
either QuickBASIC or PDS 7.0 to be used on a network in the file
sharing, record locking mode.
For further information, contact:
CDP Consultants
1700 Circo Del Cielo Drive
El Cajon, CA 92020
619-440-6482.
The QBNews Page 10
Volume 1, Number 4 September 3, 1990
Don Malin's Cross Reference Program
Crescent Software has announced a sophisticated cross-reference
program for BASIC programmers. Unlike other cross-reference utilities
that merely list each variable, Crescent's XREF provides a wealth of
useful reports that show exactly what is going on in a program.
Highlights include a Call Tree report which shows the
interrelationships between all procedures. This report may be
organized either alphabetically, or logically (in order of
occurrence). Further, line numbers may be shown relative to the
beginning of procedures like the QuickBASIC environment, or relative
to the beginning of the source file. XREF also prints a Table of
Contents for the entire listing, showing the starting page number for
each procedure. All reports may be sent to a printer, disk file, or
browsed and scrolled on-screen.
According to Crescent, XREF is extremely easy to use, relying on
pull-down menus and dialogue boxes for all program operations. XREF
reads the main program's .MAK file, and uses that to examine all the
files that comprise an entire application automatically. This
includes modules that are external to the main BASIC program, and
$INCLUDE files. XREF understands SHARED, COMMON, COMMON SHARED, DIM
AS, DEFINT, and all of the other DEF-type statements. The program is
compatible with all versions of Microsoft BASIC including BASICA and
GW-BASIC, so it is ideal when converting a large program from those
earlier BASIC dialects.
XREF also includes a unique utility to extract all quoted strings
and remarks and write them to file, for proofing with any popular
spelling checker. A companion utility will then remerge the corrected
text back into the source file, with the original spacing intact.
This feature also simplifies translating a program into a foreign
language.
The Object Summary report lists all of the program objects, such
as BASIC key words, procedures (SUBs and FUNCTIONs), simple variables,
TYPE variables, and both Static and Dynamic arrays. Program objects
are grouped by their type, and displayed alphabetically. Each object
is listed with information about where it was defined, and the number
of times it was used. Other reports show BASIC key word usage, and
indicate which variables are passed as parameters, and which are
Static, Dynamic, Automatic (local stack variables), and which are
Shared.
XREF requires any version of Microsoft BASIC; 512K RAM (640K plus
64K EMS recommended but not mandatory); DOS 2.1 or later; and a
PC/XT/AT or compatible computer. Full BASIC source is provided,
however Crescent's QuickPak Professional library is needed to
recompile the XREF program. 5-1/4 inch disks are standard, although
3-1/2 inch disks are available if specified when ordering. XREF costs
only $59.00, which includes free technical assistance and a
comprehensive manual with tips and tricks for program optimization.
The QBNews Page 11
Volume 1, Number 4 September 3, 1990
Crescent Software
32 Seventy Acres
West Redding, CT 06896
Phone: 203-438-5300 FAX: 203-431-4626 CompuServe: 72657,3070
The QBNews Page 12
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
P o w e r P r o g r a m m i n g
----------------------------------------------------------------------
Peeking at DOS with P.D.Q. by Ethan Winer
When most programmers consider writing a TSR program, they
immediately think of having to learn C or assembly language. DOS was
never intended to support multiple applications operating
simultaneously, and creating a TSR involves some extremely tricky
programming. To answer this need my company, Crescent Software, has
developed an add-on library for use with QuickBASIC and BASIC 7 that
lets BASIC programmers write several types of TSR programs which take
as little as 3K of memory when resident. P.D.Q. can also be used to
write non-TSR programs as well, with .EXE file sizes starting at 350
bytes. Our purpose here, however, is to examine an interesting and
useful utility that we provide as a TSR programming example with the
product. A ready to run version of this program is also provided in
the file DOSWATCH.EXE.
P.D.Q. supports three different ways to create a TSR program
using BASIC. The first uses a "simplified" method, whereby the
programmer specifies a hot key, and a location in the program that is
to be executed each time that key is pressed. Once the program
receives control, it may freely open and access disk files, regardless
of what the underlying application is doing at the time. Because DOS
is not reentrant, this is normally a very difficult programming feat
to accomplish. Indeed, entire books have been written about the
various complexities of writing TSR programs. Therefore, much of the
appeal of P.D.Q. is that it does all of the hard parts for you through
the supplied assembly language routines. An example of this type of
TSR is in the accompanying article for TSRTerm.
The second type of TSR lets you manually trap system interrupts,
and with the ease of BASIC accomplish virtually anything that could be
done in assembly language. Of course, the programmer must understand
how interrupt services are accessed, and I often recommend Peter
Norton's excellent "Programmer's Guide to the IBM PC" published by
Microsoft Press. The third type of TSR lets you combine the
flexibility of manual interrupt handling with the safety of the
simplified hot key methods. In the DOSWATCH program I will describe
here we will manually intercept DOS Interrupt 21h (Hex) to provide a
"window" into its inner workings.
P.D.Q. TSR INTERRUPT HANDLING SERVICES
There are many routines provided with P.D.Q. to support interrupt
handling, and several of these will be described. Also, for each
interrupt that a program will be handling, an 18-element TYPE variable
must be defined. This TYPE variable is used to hold the CPU's
registers, as well as the address and segment to use if the original
interrupt will be accessed. The Registers TYPE variable is very much
The QBNews Page 13
Volume 1, Number 4 September 3, 1990
like the TYPE variable BASIC requires when using CALL INTERRUPT, in
that the various CPU registers may be assigned and examined. Several
additional TYPE elements are also defined, which are specific to
P.D.Q. For example, the segment and address of the BASIC interrupt
handling code are stored there.
We'll begin by briefly examining the P.D.Q. TSR routines used in
DOSWATCH. The first is PointIntHere, and it indicates where in the
BASIC program to begin executing when the specified interrupt occurs.
Because BASIC does not provide a direct way to obtain the address of a
particular statement or line, a call to PointIntHere must be
immediately followed by a GOTO. The very next program statement will
then receive control each time the specified interrupt occurs.
The next two routines are IntEntry1 and IntEntry2, and these must
be the very first two statements in the body of the BASIC interrupt
handler code. These routines copy the current CPU register values
into the TYPE variable, so they may be examined and set by your
program. (Internally, IntEntry1 saves the current value of the AX
register and establishes "DGROUP addressability", so variables within
the TSR program will be accessed correctly. IntEntry2 then copies the
remaining register values into the TYPE variable, and jumps to the
correct location in the TSR program.)
IntEntry2 also expects an "Action" parameter, which tells it what
to do if another interrupt occurs before you have finished processing
the first one. There are two options -- pass control on to the
original interrupt handler, or ignore the interrupt entirely and
simply return to the caller. The only situation in which a second
interrupt could occur like this is when trapping hardware interrupts
such as the timer tick or the keyboard or communications interrupts.
While a program is processing the interrupt, it may call one of
the three P.D.Q. interrupt service routines. The first of these is
GotoOldInt, which passes control to the original interrupt handler.
GotoOldInt is used when a program is intercepting only certain
services, and the current service is not one of those. In that case
you would want to pass control on to the original (or subsequent)
handler. For example, if you are intercepting DOS interrupt 21h and
care only about, say, service 4Eh, then you would call GotoOldInt for
all of the other services so DOS could handle them.
The second routine is CallOldInt, and this routine lets you call
the original interrupt as a subroutine, and then receive control again
when it has finished. This would be useful when intercepting the
printer interrupt, for instance, perhaps to test the success of the
last print attempt.
The final interrupt handling routine is ReturnFromInt, which
returns control to the underlying application. ReturnFromInt would be
used when your program has processed the interrupt entirely by itself,
and no further action is needed by the original handler. Neither
CallOldInt nor ReturnFromInt are used by DOSWATCH, and these are
The QBNews Page 14
Volume 1, Number 4 September 3, 1990
described solely for your interest.
INSIDE DOSWATCH
The DOSWATCH program shows how to intercept DOS Interrupt 21h,
and it also serves as a good example of writing a general purpose
interrupt handler using P.D.Q. DOSWATCH is a TSR program that
monitors every call made to DOS Interrupt 21h, and then displays
information about the current service that is being requested. The
service number is printed in the upper left corner of the screen, and
in many cases additional information is also shown. Watching DOS as it
works can be very enlightening, and DOSWATCH lets you view not only
the activity of your application programs, but also DOS itself. The
DOSWATCH.BAS source listing is shown in Figure 1.
DOSWATCH begins by including PDQDECL.BAS, which declares all of
the available P.D.Q. extensions and defines the Registers TYPE
variable. Next, a unique ID string is defined, which in this case is
also the sign-on message. P.D.Q. uses the ID string internally to let
you check for multiple installations. However, that feature is not
exploited here for the sake of simplicity.
Because DOSWATCH may receive control at any time, it is essential
that the regular BASIC PRINT statement is not used. Unlike the
simplified TSR method which allows nearly any BASIC statement to be
used freely without regard to the current state of DOS or the BIOS,
DOSWATCH must use the PDQPrint routine that writes directly to video
memory. PDQPrint is a "quick print" routine, and it expects the row
and column as passed parameters. Therefore, we must use CSRLIN and
POS(0) to know where that is. (P.D.Q. does in fact allow TSR programs
to perform nearly any service within a manual interrupt handler, but
an additional call is needed and DOSWATCH doesn't truly need that
feature.)
The next two statements define the strings that will receive the
information message, and the file or directory name when appropriate.
The statements that follow establish several variables that will be
used by the program. Using variables as parameters is always faster
than constants, because BC.EXE generates extra code to store constants
in memory each time they are used. Likewise, assigning Zero$ once
eliminates repeated references to CHR$() each time INSTR is used later
on in the program. This is true for both P.D.Q. and regular
QuickBASIC.
Although it may not be obvious, using INSTR(DOSName$, Zero$) is
faster and creates less code than INSTR(DOSName$, CHR$(0)), because
CHR$() is actually a called routine. A few microseconds either way is
unlikely to matter in most programs, but in a TSR that steals
interrupt processing time, speed is at least as important as program
size. (Defining and assigning Zero$ adds a dozen or so bytes.)
The last preparatory step is to assign Registers.IntNum to the
The QBNews Page 15
Volume 1, Number 4 September 3, 1990
value &H21, to specify which interrupt is to be trapped. Finally,
PointIntHere is called, followed by a GOTO to the very end of the
source listing where the program is installed as a TSR by calling the
EndTSR routine.
The remaining program statements will now be executed every time
an Interrupt 21h occurs. The first two steps are calls to IntEntry1
and IntEntry2, and this is mandatory in all TSR programs that do not
use the simplified method. These routines simply save the current
processor registers values, so they can be restored when DOSWATCH
passes control to DOS later on. The next two steps clear the Ticks
variable, and derive the current service number from the AX register.
For DOS services that process a file or directory name, DOSWATCH
pauses for a half-second allowing time to read the name. Ticks
specifies the number of timer ticks in 18ths of a second. For other
services only the service number is displayed, and there is no added
delay.
Fifteen different DOS services are recognized, and these are
filtered through a SELECT/CASE block. All of the supported services
assign Message$ to the appropriate text, and those services that
process a file or directory name also use the GetDOSName subroutine.
GetDOSName copies the current name into the DOSName$ variable, and
also sets Ticks to 8 to allow time to read it. In some cases,
additional information is assigned to Message$, for example when
reporting a drive letter or file handle number. This information is
taken from the appropriate registers as necessary. If a particular
service is not supported, then Message$ is simply cleared to blanks in
the CASE ELSE code.
Because DOSWATCH is not actually processing any of the DOS
services, the final step is to call GotoOldInt, which in turn jumps to
the internal DOS function dispatcher. Thus, DOSWATCH displays what is
about to happen, before DOS actually receives control.
The remaining statements comprise the GetDOSName subroutine,
which copies the current file or directory name into the DOSName$
variable. In this case, the P.D.Q. BlockCopy routine copies the first
50 characters pointed to by DS:DX into DOSName$, and then INSTR is
used to locate the CHR$(0) which marks the end of the name. Only
those characters that precede the zero byte are retained, and the
LEFT$ assignment clears any characters that follow the name.
Running DOSWATCH once will install it as a TSR, and all
subsequent DOS operations will then be displayed on the top line of
the screen. Observing DOSWATCH as it works can provide much insight
into DOS' internal operation. For example, you will no doubt find it
enlightening to start QuickBASIC and load a program such as DOSWATCH
itself. This shows DOS at work as it loads and executes QB.EXE, and
then you can watch QuickBASIC as it loads the main program and the
PDQDECL.BAS Include file. Equally interesting is all the unnecessary
DOS activity QuickBASIC performs to obtain a list of file names when
you select Alt-F-L from the pull-down menu.
The QBNews Page 16
Volume 1, Number 4 September 3, 1990
You could also modify DOSWATCH to pause briefly for all of the
DOS services rather than just some of them. Even though the operation
of your PC will be slowed dramatically, many varied and interesting
facets of DOS will become apparent. For example, each time you use
the DIR command, DOS (actually COMMAND.COM) always changes to the
current directory, opens the directory "file", and then writes the
directory information to Handle 1. The added delay will also let you
observe DOS as it closes all available handles each time a program
terminates.
As you can see, besides providing an excellent way to learn more
about TSR programming, DOSWATCH is also a very useful utility program
in its own right. One of our primary goals in developing P.D.Q. was to
allow BASIC programmers to create programs as small and fast as those
written in C. With a minimum .EXE file size less than one-fourth that
of QuickC and Turbo C, I believe we have met that goal.
[Editor's note: P.D.Q. costs $129 plus $6 for 2nd day shipping, and it
is available from Crescent Software at the address shown below.]
**********************************************************************
Ethan Winer is President of Crescent Software, and is the author
of their QuickPak Professional and P.D.Q. products. Ethan may be
reached at 32 Seventy Acres, West Redding, CT 06896, 203-438-5300; on
CompuServe at 72657,3070; and on MCI Mail as EWINER.
**********************************************************************
The QBNews Page 17
Volume 1, Number 4 September 3, 1990
A Pop-Up Communications Program with PDQ by Dave Cleary
One of the most asked questions I have heard is "How do I make a
QuickBASIC program a TSR?". That is the reason I chose to devote an
issue of The QBNews on how to accomplish this. With the help of
Crescent Software's PDQ or Microhelp's Stay-Res Plus, TSR programming
is made easy for QuickBASIC programmers.
I am going to explain a Pop-Up communications program that is
included as a demo with PDQComm. PDQComm adds communications support
to PDQ and it also fixes some of the "features" that Microsoft has
built into QuickBASIC's internal communications support. TSRTerm is a
PDQ simplified pop-up. It is called simplified because all the hard
work of making sure your TSR won't crash the system is taken care for
you.
The first thing you need to do when creating a simplified PDQ TSR
is to create an ID$. PDQ uses this string to determine if the program
is already installed and also is used when the program is de-
installed. It is important that this is your first string assignment
because QB dynamically moves strings around as they get added and
deleted and ID$ can not move. ID$ also has to be unique. One good
practice is to use the program's name and version number as the ID.
Next you need to specify the hot key. The hot key is stored in a
normal integer and is specified in two portions. They upper byte is
the shift mask while the lower byte is the scan code for the key
desired. The shift mask lets you select the Alt, Ctrl, Left Shift, and
Right Shift to be used with your hot key. You can also OR the values
of the shift mask together to get 3 or 4 key combinations such as
Ctrl-Alt-C. I have chosen Alt-C for my hot key. Specifying the hot key
in hex with &H makes it easier to work with. &H8 is the shift mask for
the alternate key while &H2E is the scan code for the C key.
Right after I specify the hot key, I check to see if the program
is already installed. This is done by a call to the TSRInstalled
function, passing it your ID$. If the program is installed,
TSRInstalled returns the DGROUP segment where it was found. This is
used for deinstalling the TSR later in the code. Otherwise it returns
a 0. We then do some port initialization with PDQComm routines. You
may notice that the PDQComm routines mimic QuickBASIC's as much as
possible. This was done to make PDQComm easy to integrate into your
QuickBASIC program. BTW, PDQComm also works with regular QuickBASIC
and PDS 7 far strings.
Now comes the hard part of using PDQ. You make a call to
PopUpHere with your hot key and ID$. You must then put a GOTO
immediately after the call to PopUpHere. The GOTO jumps over the code
that will be executed when it pops up. All you have to do is make a
call to EndTSR and your program is now resident. Well, that isn't too
hard. That is what makes PDQ so good. It is very simple to make all
kinds of TSR's.
The QBNews Page 18
Volume 1, Number 4 September 3, 1990
When you invoke the hot key of your TSR, control will be given to
your program right after the GOTO statement. Will you are popped up,
you can use almost any QuickBASIC statement you want without worrying
about crashing your system. The only restrictions you have is the use
of INPUT and INKEY$. PDQ gives you replacements for these routines,
BIOSInput and BIOSInkey, so you can easily get around this
restriction.
Next, you need to know how to return control to the underlying
application. This is done with a call to PopDown. PDQ doesn't save and
restore screens for you so you need to do it yourself. That is what I
am doing in the statements preceding the call to PopDown. I restore
the cursor position and screen that I had saved when the program had
popped-up.
All that leaves now is the PDQ routine used to deinstall your TSR
from memory. That code is under the label Deinstall. In this program,
I have chosen to deinstall it by executing it again at the DOS prompt.
If TSRTerm finds itself already installed, it will deinstall itself
from memory. This is done with a call to PopDeinstal function. You
pass this function the segment that was determined by the call to
TSRInstalled and your ID$. It will then tell you if your program was
successfully deinstalled or not. That is all there is to programming
pop-up TSRs with PDQ. PDQ and PDQComm is available from Crescent
Software for $129 and $49 respectively. You can contact Crescent at
203-438-5300.
**********************************************************************
Dave Cleary is an electronics engineer and programmer for Vectron
Laboratories in Norwalk, CT. He is also the author of PDQComm and
editor of this newsletter. He can be reached in care of this
newsletter, on Compuserve as 76510,1725, on Prodigy as HSRW18A, or on
the Crescent Software Support BBS 203-426-5958 1:141/777.
**********************************************************************
[Editor's Note]
Since TSRTerm was meant only for demonstration purposes, the baud
and dial prefix is hardcoded in. These are 1200 and ATDT respectively.
You will need to recompile and link with PDQ and PDQComm to change
these. All files are located in TERM.ZIP.
The QBNews Page 19
Volume 1, Number 4 September 3, 1990
Using Stay-Res Plus to make TSR's by Larry Stone
Have you ever had the need to create a program that is RAM
resident? Needless to say, it is impossible to do it directly in
BASIC because of the controlling influence of the BASIC engine. This
doesn't mean that it can't be done, it means that it can't be done
directly. There are two commercial products available that can allow
your QB programs to become RAM resident. One product, PDQ is offered
by Crescent Software and the other, Stay-Res Plus by MicroHelp, Inc.
I own both products and highly recommend each.
I use PDQ for small, memory resident utilities - those programs
whose DOS kernels are no larger than 15K bytes. But, most of what I
code turns out to be full-fledged application programs. Not little
utilities performing a single minded task, rather, my programs tend to
be large applications that have multiple menus and perform multiple
I/O functions. Stay-Res Plus excels in this arena.
Stay-Res Plus, coupled with a little additional effort by you,
can turn your application into a TSR (Terminate and Stay Resident).
A Stay-Res Plus assisted application can pop-up with the press of up
to 23 pre-defined hot keys, on a specific date and time, on the "ring
detect" from a communications port, or even from a POKE by your other
programs. Your Stay-Res program can even SHELL other applications
if popped-up over DOS. And, most important, when your program goes to
sleep, it only locks out a 10K - 11K byte DOS kernel, sending the rest
of the memory "image" to either EMS, or to temporary disk files (note
Stay-Res will allow the entire application to be DOS RAM resident).
To write a Stay-Res application demands of you to conform to some
basic (no pun intended) rules. Your program needs an initialization
section where all of your arrays and TYPEs are DIMed, and CONST's are
defined, and where all of the necessary logic required is performed
before entering the main program section. Here is where your program
displays it's message that it is going to sleep. Your program also
needs a memory-resident section which the main section will transfer
control to, every time it pops down.
Because your program will be TSR, there are a few do's and don'ts
you must adhere to. Never, ever ERASE or REDIM an array. This is due
to how BASIC will handle these commands. Issuing these commands will
allow BASIC to move memory around. Once this happens, Stay-Res cannot
protect the memory pool and your program will probably crash. If you
need to clear a numeric array, place it into a FOR...NEXT loop and set
each element of the array to zero. String arrays would be set to null
Dynamic arrays also need special attention due to the manner that
BASIC allocates their memory. QB4, BASCOM6, or PDS will store dynamic
arrays downward from the top of the heap (high memory) instead of just
above the stack (bottom of the heap), leaving a gap in between. In
order for Stay-Res to protect this memory, you must instruct QB to put
these arrays just above the stack, making all of the program's memory
contiguous. You do this by using BASIC's SETMEM instruction followed
The QBNews Page 20
Volume 1, Number 4 September 3, 1990
by a call to SrAutoSetBlock. A note for all of those Stay-Res users
who may be reading this. Stay-Res v3.3 has an undocumented command,
SrAutoSetBlock. This command can be used in lieu of SrSetBlock and is
much more efficient. Simply DIM and define every variable possible at
the top of your code (pretend your programming in Pascal), then issue
the command HeapMem& = FRE(-1). Subtract a fudge factor from HeapMem&
to account for anything missed or for what variable length strings may
require: HeapMem& = HeapMem& - 2048 then have BASIC set the memory
contiguous: MemNeeded& = SETMEM(-HeapMem&) and follow up with the call
CALL SrAutoSetBlock(Paragraphs%, Ecode%). Then you should check if it
will work with: IF MemNeeded& <= HeapMem& OR Ecode% <> 0 THEN PRINT
"Not Enough Memory - Bye Bye".
One important question to consider is which BASIC commands cannot
be used with Stay-Res. Well, under no circumstance can you ever use
BEEP or PRINT CHR$(7), or BASIC's RUN or SHELL statements once your
program has become memory resident the first time (use the alternative
SHELL's supplied with Stay-Res). You can use SOUND or PLAY so long
as you allow sufficient time for the speaker to shut off before going
to sleep. However, when Stay-Res pops up with an Ecode% equal to 1
then DOS is, at this time, busy and you cannot use LOCK, UNLOCK, DATE$
TIME$, PEN, LOF(), EOF(), PLAY, LPRINT, RANDOMIZE TIMER, and all other
file related commands except LOC(), and all directory commands. So,
what does this mean? The manual trys explain it with an example to
instruct DOS to copy a *huge* file then pop-up your Stay Res program
in the middle of the COPY. I tried that and, on my system, DOS simply
suspended the COPY. Stay-Res did not pop-up with Ecode = 1 and, when
my program popped down, DOS continued as if it were never interrupted.
I even tried this procedure with PKzip and it too simply suspended
it's operation until I popped down. I suspect that on my computer, it
would have to be networked with local I/O activity before an Ecode of
1 would be returned by Stay-Res. Never-the-less, if DOS is busy, set
a flag and if the user needs any of the commands listed, print a
message to try later when DOS isn't so busy.
Compiling and linking Stay-Res must be done from the DOS command
line. Compiled programs must use the /O switch and the linker must be
instructed to place the Stay-Res object first and a supporting object
last. The link command should look like:
LINK STAYQB4+prog+otherMods+MHMISCP,prog,nul,externalLibs;
The term, prog stands for the name of your program. Notice that prog
is repeated mid-way in the command and separated by commas. This will
instruct the linker to name the completed EXE with your program name.
To facilitate your coding TSR's, MicroHelp supplies a template
program with clear, concise, pre-defined areas and GOSUB instructions
required. If you can paint by numbers then you can use this template.
Creating a TSR is not a snap but, neither is it very difficult if you
adhere to the rules.
To illustrate the power of Stay-Res, accompanying this article is
The QBNews Page 21
Volume 1, Number 4 September 3, 1990
a full function, scientific and statistical calculator with built-in
mouse support. The calculator has 72 buttons supporting 92 functions
such as, trigometric computations (including inverse and hyperbolic),
log e, log 10, and log n, angular conversions, binary, octal, complex
and hexidecimal number support, a 500 element array for statistical
computations, permutations, combinations, four types or means, nth
roots, standard deviations, a 512 line user configurable look up table
and, not-to-mention, factorials and random number table whose sequence
far exceeds BASIC's and offers three types of random number - defined
range randoms, randoms with an exponential curve defined by your mean,
and randoms with a normal curve defined by your mean and your standard
deviation. The EXE size is 129,440 bytes and requires some 204,000
bytes to load. The program incorporates ERROR trapping in several
subprograms and functions. However, when it pops down, it reduces it-
self to a 10 Kbyte kernel!!!
I and my beta testers found the calculator to be 100% compatible
with QB, QBX, Lotus 123 v2.1, MS WORD, Word Perfect, Qedit, PC-Write,
dClip, CLIPPER applications, dBase, LIST, et. al. In fact, the only
program that it won't pop up over is Code View or Lotus v1.1 because
these are not "well-behaved" programs (Microsoft broke their own
compatibility requirements when they constructed Code View). If a
Stay-Res program is not compatible with another program, I assure you
the fault is that the other program is not well-behaved.
**********************************************************************
Larry Stone is President of LSRGroup and is involved in writing
software for marine and aquatic research. He can be reached at
LSRGroup, P.O. Box 5715, Charleston, OR 97420, or in care of this
newsletter.
**********************************************************************
[Editor's Note]
When I asked Larry to do a pop up calculator, I expected a small
4 function little program. Well, Larry isn't known for a lack of
effort and has come up with a truely professional program. Because of
the time and effort that has gone into the program, I feel that it is
only right for him to offer it as shareware. But, as readers of the
QBNews, you also get a special bonus when you register. Just mention
that you read the QBNews and Larry will send you full source code when
you register. The file CALC.ZIP contains an abridge version of the
documentation. To get the full documentation, you can download it from
these fine boards:
The Crescent Software Support BBS 203-426-5958 2400 Baud
The Empire Builder 503-888-4121 2400 Baud
The QBNews Page 22
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
A l g o r i t h m s
----------------------------------------------------------------------
Improving your IQUEUE by Jim Mack
With all that's been added to the BASIC language in the past few
years, it might be hard to believe that there's a more-or- less
elementary data structure that's been ignored. Actually, "ignored" is
a bit strong, since you can build this structure out of more primitive
elements using the core language, but I'll bet most of you never have.
You've probably gathered from the title that what I'm talking
about is a data stack. Now, whether a stack is an essential data
structure is certainly debatable: after all, aren't high-level
languages supposed to insulate you from such machine-like things as
registers and stacks? And haven't you gotten along for years without
a queue?
If you've never programmed in MASM (or Forth), you might be
blissfully unaware of the possibilities of just "pushing" data
somewhere and later "popping" it back... and of the concept of feeding
data to another process faster than it can be absorbed.
Well, you're in for a not-too-rude awakening here, as we explore
the world of buffered data. We'll discover two useful sets of
routines and see how you might apply them to some common programming
tasks.
"Queue". The word is so weird-looking, let's get it out of the
way first. Pronounced "Q" (then why are all those other letters
there?), it's just another word for a "line" such as you might
encounter at a theater or bank. We used to say as children "it forms
at the end"... not strictly true, but close enough. People (or
objects) are "handled" in the same order that they join the line:
first come, first served. Another common way of saying this is FIFO
(arf!) or first-in, first-out.
If there's room in a theater lobby for 20 people to wait in line
("on" line for you New Yorkers), then we can think of the lobby as
buffering 20 items. When the lobby is full, no one may enter until
someone leaves at the head.
It's the same for data. If one process generates data items at
roughly the same rate that another process uses them, but with bursts
of activity followed by periods of calm, a buffer can smooth out the
peaks. The originating process can place items into a queue as fast
as it likes, while the receiving process can take time to work with an
item, and get a new one as it requires.
A good example of this activity is QB's RS-232 serial COM.
Characters arriving at the port interrupt normal processing and are
quickly placed into a queue. As your program becomes aware of them,
The QBNews Page 23
Volume 1, Number 4 September 3, 1990
it retrieves characters and, well, does what it does with them. If
your program empties the bucket roughly as fast as it's filling up,
then the queue provides some leeway for those characters which might
need a little more processing. The larger the buffer, the "spongier"
the process can be.
There are two ways of handling buffered data. One way is to let
the buffer fill up, then disallow any more input until it's emptied
again. A movie theater functions like that: if you don't get in at 6,
you wait until 8... and nobody goes in at 8 until the 6 o'clock crowd
has left.
Far more useful for data is the "circular" queue, often called a
ring buffer (note to jewelers: I know this isn't what *you* call a
ring buffer. Don't write).
There are analogies for both in the real world. The theater line
is a straight queue. A service department might instead use a "take a
number" scheme with tickets numbered from 1 to 100. Once number 100
is serviced, number 1 is next, and so on.
"Take a number" also introduces the idea of a pointer. In a
theater, the entire line moves forward whenever someone leaves from
the head. In the service department, folks may move around until
their number comes up.
In data terms, the "theater" method is terribly inefficient.
Instead, we use pointers to indicate where the current head and tail
of the queue are. With pointers, the size of the queue doesn't affect
the speed of operations, so enqueueing and dequeueing data items can
be very fast.
We turn an ordinary queue into a circular one by the simple
action of "wrapping" the pointers, or in more technical terms, by
incrementing them MOD the length of the queue... 99 + 1 = 100, but 100
+ 1 = 1.
A queue has two pointers. The "write" pointer indicates where in
the buffer any new item will be stored. The "read" pointer gives the
location from which the oldest item will be retrieved. These are
circular if they're wrapped around when about to point off the end.
The queue is full if incrementing the write pointer would cause it to
equal the read pointer.
So now, at least in concept, you know all about the queue. It's
a form of buffer, a FIFO stack. It's really that simple... but not as
simple as the LIFO (last-in, first-out) stack.
To review what a LIFO stack is, we move from the theater to the
restaurant (almost like real life, isn't it?). Norton's First Law of
Computer Explication states, in part, that "...every treatise on The
Stack will begin by discussing dinner plates...", so here we go.
The QBNews Page 24
Volume 1, Number 4 September 3, 1990
Ever eaten in a cafeteria? Sure you have. Then you've seen
those "plate-o-matic" devices that, when you take the top plate,
another one springs up behind it...? Let's perform what nuclear
theorists call a "thought experiment". How do plates get there in the
first place? Do you suppose the cafeteria, having studied queues,
replaces them at the bottom as they're taken off the top?
Right. They operate on a last-washed first-dirtied basis, just
like the stack we think of in CPU terms. Items are "pushed" onto a
LIFO stack, and later "popped" back off. Technically, a FIFO queue is
a stack too, but we usually reserve the word "stack" for the LIFO
version.
A LIFO stack can be managed with only one pointer: where the next
pushed item will go or from where the next popped item will come. You
could shuffle all the data each time you push or pop a data item, but
as we saw with queues, a pointer is a lot more efficient.
So, a stack is a temporary holding area for data. It can be used
for other things, but that pretty much sums it up.
It's tough to come up with any other real-world example of a LIFO
stack. Fortunately most of us are already familiar with the idea
because nearly every computer operation involves some use of the CPU's
system stack.
But while we realize how vital a stack is for the CPU, what does
it buy us at the program level to have our own stack? After all, in
QB if we need somewhere to put an item aside temporarily, why, we just
reference a new variable... it might even be created on the system
stack, and we'd never need to know.
Well, there are times when an algorithm runs more smoothly, or is
easier to implement, using a stack. Too, you may not want the
overhead of another temporary variable (or ten), or may not want to
dream up Yet Another Unique Variable Name. If so, the stack's the
ticket. A data stack can also allow you to perform some recursive
operations within an otherwise STATIC procedure.
One example of a technique well suited to a stack is Help. I've
written a "hyper-help" utility, wherein the user can follow links
embedded in the text to jump from one topic (page) to another in any
sequence that pleases her. Keeping track of the path being followed
so we can unwind to any point (and branch again from there), is
potentially a nightmare. But with a stack the job is trivial:
whenever she negotiates a link, we just push the current location.
When she wishes to back up, we pop page locations back off the stack.
Simple, direct, and obvious.
A queue can find use in passing a variable number of arguments to
a procedure, something QB can't do on its own. Put as many items into
the queue as you like... the called procedure just pops them out until
there are no more. Queues find their best use with asynchronous
The QBNews Page 25
Volume 1, Number 4 September 3, 1990
processes, for example when passing data from an interrupt handler to
a program, or vice versa.
Now for the details. I've written this so there's one stack and
one queue, each 255 items long. The code for them is in IQUEUE.ASM
and ISTACK.ASM, two small MASM files which define several new QB SUBs
and FUNCTIONs, and which reserve the space for the buffers.
There's nothing to stop you from using more than one of each, but
you'll have to extend the concept on your own. Once you've had a look
at the MASM code, you'll see just how simple all this really is.
There are three nice things about doing it in MASM. First, it
takes up no precious DGROUP data space (well, 6 bytes for pointers,
but who's counting?). The reason we don't need any DGROUP space for
the buffers is that we put them into the code segments we declare for
the procedures.
Second, everything about these is automatically global to every
procedure in any module which DECLAREs them. This applies to linked-
in libraries, run-time libs... anything which "knows" about these can
share the procedures and the data without resorting to COMMON or
SHARED declarations.
Third and most important, it "encapsulates" the queue and the
stack, so you can tell everyone you're using "object-oriented
techniques" in your programming. Of course I'm joking, right? Well,
only a little...
Here are the DECLAREs you'll need to include. For IQUEUE:
DECLARE SUB IQflush ()
DECLARE SUB IQput (intvalue%)
DECLARE FUNCTION IQget% ()
DECLARE FUNCTION IQfree% ()
DECLARE FUNCTION IQavail% ()
DECLARE SUB ISclear ()
DECLARE SUB ISpush (intvalue%)
DECLARE FUNCTION ISpop% ()
DECLARE FUNCTION ISfree% ()
DECLARE FUNCTION ISavail% ()
You can use these for integers or characters. The listings
include versions for long integers as well. Naturally, you can use
the long-integer version for smaller objects too, but you'll have to
explicitly convert from integer to long, etc, to do that.
If you don't like these names, or if they conflict with names
you're already using, you can use ALIAS to rename them without going
to the bother of editing and reassembling the MASM code. For example:
The QBNews Page 26
Volume 1, Number 4 September 3, 1990
DECLARE SUB PushError ALIAS "ISPush" (intval%)
You can then use PushError instead of ISPush to refer to the same
routine.
So let's see how these work in QB. The IQFlush procedure does
what its name implies: it empties the queue of all data. For speed,
all it really does is reset the pointers, since the contents of the
queue don't matter if the pointers say it's empty. So to start from
scratch at any time, just say:
IQFlush
To place an element into the queue is just as easy:
IQPut AnyInteger%
IQPut ComplexType.IntArray(elno)
IQPut SADD(j$)
IQPut (((1000 * 2) \ 3) + 7) ^ 2
Anything which evaluates to an integer can be put into the queue,
but watch it... if the queue is full when you try to PUT an element,
you'll generate an Overflow Error in QB. If this might be a problem,
use the IQFree function:
IF IQFree THEN 'meaning "if there's any room"
IQPut MyElement%
END IF
Note that the reason you get an Overflow Error is because that's
the way I designed it: if you want the code to whistle "Lara's Theme"
or format your hard disk, you can replace my very minimal QUERR with
your own error handler. Overflow is an easy error to generate, and it
does more or less correspond with this condition.
To retrieve an element from the queue, use IQGet. You can treat
IQGet just as though it were a simple integer:
PRINT IQGet
k = IQGet MOD 3
CALL MySub(r$, hpos%, IQGet%)
But as with PUT, if you try to GET when the queue is empty,
you'll get an Overflow. To guard against that, use IQAvail:
IF IQAvail THEN 'meaning "if anything's there"
k = IQGet + 19
END IF
And that's it. The STACK routines are exactly analogous to the
QUEUE routines, so there's no need to review them separately. The MASM
listings are commented well enough that if you need to modify them,
The QBNews Page 27
Volume 1, Number 4 September 3, 1990
you can. You'll need MASM 5.1 to reassemble these if you make
changes.
Two notes: to make underflow and overflow detection easier in the
IQUEUE procedures, the actual number of elements you can store at once
is one less than the EQU'd size of the queue. And if you intend to
use the queue with interrupts, you'll want to surround any pointer
operations with CLI/STI to avoid collisions. If you run into any
problems, you can reach me via CompuServe at 76630,2012, or on BIX as
"jsmack". Your comments are always welcome.
**********************************************************************
Jim Mack is a programmer specializing in real-time systems for the
entertainment industry. He can be reached via CIS ID 76630,2012 on
the MSSYS forum in the BASIC or MASM sections, or at Editing Services
Co., PO Box 599, Plymouth MI 48170, (313) 459-4618
**********************************************************************
[EDITOR'S NOTE]
You can find the ASM source and assembled .OBJ's for this article
in the file IQUEUE.ZIP.
The QBNews Page 28
Volume 1, Number 4 September 3, 1990
----------------------------------------------------------------------
I n p u t P a s t E n d
----------------------------------------------------------------------
Due to demand, you can now get the QBNews mailed to you on disk for a
nominal charge. The rates are as follows:
United States $3.00
North America (outside US) $4.00
Outside US $6.00
Please add $1.00 if you want it on 3.5" media. Also, you can receive
it in either ZIP, PAK, or LHZ format. If you don't specify, you will
receive it in ZIP format.
The first time you request the QBNews, you will receive all available
issues. That way you won't miss any.
And of course, you can always download it free from Treasure Island.
First time callers are granted limited download privlidges so you can
get it. Treasure Island is 9600 HST, is at FIDO address 1:141/730, and
its phone is 203-791-8532. To request your copy on disk, send US funds
to:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
Remember to specify which archive format you want.
The QBNews Page 29
Volume 1, Number 4 September 3, 1990
WE NEED AUTHORS!
If you are interested in writing for the QBNews, you can contact
me at the address below. I can also be reached on Compuserve as
76510,1725 or on Prodigy as HSRW18A. I am also the Sysop of the
Crescent Software Support BBS, 203-426-5958 1:141/770, so I can be
reached there also. If you are submitting articles, Iask that they be
ASCII text with no more than 70 characters per line.
You can write me at:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
David Cleary
The QBNews Page 30